#pragma once

#include <vector>
#include <string>
#include <algorithm>

// 网络库底层的缓存类的定义
class Buffer
{
public:
    // 在构造函数的时候，默认让m_readerIndex与m_writerIndex都初始为8，缓冲区大小为8 + 1024（后续缓冲区的长度不够，会扩容）
    explicit Buffer(size_t initialSize = kInitialSize) : m_buffer(kCheapPrepend + initialSize)
                                                       , m_readerIndex(kCheapPrepend)
                                                       , m_writerIndex(kCheapPrepend)
    {

    }           

    // 返回可读缓冲区的长度
    size_t readableBytes() const
    {
        return m_writerIndex - m_readerIndex;
    }         

    // 返回可写缓冲区的长度
    size_t writableBytes() const
    {
        return m_buffer.size() - m_writerIndex;
    }      

    // 返回可读缓冲区的起始下标
    size_t prependableBytes() const
    {
        return m_readerIndex; // m_readerIndex会随着数据的读取而发生偏移，
                              // 但是始终指向可读缓冲区的起始位置，此时可能它前面的区域已经空闲了，就可以写入数据
    }

    // 返回缓冲区中可读数据的起始地址
    const char* peek() const
    {
        return begin() + m_readerIndex;
    }


    // 读取数据
    void retrieve(size_t len)
    {
        if (len < readableBytes()) {
            // 应用只读取了可读缓冲区数据的一部分长度，就是len，还剩下m_writerIndex - m_readerIndex - len的数据长度
            m_readerIndex += len;
        } else {
            // 读取的字节数 len 大于或等于缓冲区中可读的总字节数 readableBytes()，
            // 则表示缓冲区中的所有数据都被读取完毕。此时，调用 retrieveAll() 方法来重置缓冲区
            retrieveAll();
        }
    }

    // 重置缓冲区的状态，使其准备好进行下一轮的数据读写操作
    void retrieveAll()
    {
        m_readerIndex = m_writerIndex = kCheapPrepend;
    }

    // 把onMessage函数上报的Buffer数据转换成string类型的数据返回
    std::string retrieveAllAsString()
    {
        return retrieveAsString(readableBytes()); // 应用可读取数据的长度
    }

    // 从当前缓冲区中提取指定长度的数据到一个std::string对象中，并且在提取后重置缓冲区状态以准备下一次操作
    std::string retrieveAsString(size_t len)
    {
        // 读取缓冲区可读的数据，并返回一个std::string对象
        std::string result(peek(), len);
        retrieve(len); // 本次数据读取完毕，对缓冲区进行复位操作
        return result;
    }

    // 可写缓冲区 < len 需要进行扩容，确保有足够的缓冲空间
    void ensureWriteableBytes(size_t len)
    {
        if (writableBytes() < len) {
            makeSpace(len); // 进行扩容
        }
    }

    // 把[data, data + len]内存上的数据添加到writeable缓冲区中
    void append(const char* data, size_t len)
    {
        ensureWriteableBytes(len);
        std::copy(data, data + len, beginWrite());
        m_writerIndex += len; // 偏移可写缓冲区的起始下标
    }

    // 返回可写缓冲区的起始位置
    char* beginWrite()
    {
        return begin() + m_writerIndex;
    }

    // 返回可写缓冲区的起始位置
    const char* beginWrite() const
    {
        return begin() + m_writerIndex;
    }
    
    // 从fd上读数据
    ssize_t readFd(int fd, int* saveError);
    // 通过fd发送数据
    ssize_t writeFd(int fd, int* saveError);

private:
    char* begin()
    {
        return &*m_buffer.begin();  // vector底层数组首元素的地址，也是数组的起始地址
    }
    
    const char* begin() const
    {
        return &*m_buffer.begin();  // vector底层数组首元素的地址，也是数组的起始地址
    }

    // 缓冲区扩容
    void makeSpace(size_t len)
    {
        // 可写缓冲区的长度 + 前面的数据已被读取，空闲出来的数据长度 < len + kCheapPrepend
        if (writableBytes() + prependableBytes() < len + kCheapPrepend) {
            m_buffer.resize(m_writerIndex + len);
        } else {
            size_t readable = readableBytes();
            // 等价于将未读的数据向前移动，把空闲区拼凑成一块连续的可写区域
            std::copy(begin() + m_readerIndex, begin() + m_writerIndex, begin() + kCheapPrepend);
            m_readerIndex = kCheapPrepend;
            m_writerIndex = m_readerIndex + readable;
        }
    }

public:
    static const size_t kCheapPrepend = 8;    // 数据包头所占字节数
    static const size_t kInitialSize = 1024;  // 缓冲区的初始值大小

private:
    std::vector<char> m_buffer; // 缓冲区
    size_t m_readerIndex;   // 缓冲区可读数据的起始位置
    size_t m_writerIndex;   // 缓冲区可写数据的起始位置
};